昨天介紹完切片 (slice) 不同的複製方式與隱藏陣列的關聯,今天繼續來介紹有點類似的映射表 (map) 吧!
Go 語言的 map 是一種雜湊表 (hashmap) ,在陣列及切片中,我們如果需要找到元素值或是更換元素值時,都可以使用索引,那是因為陣列及切片是有序集合,索引值一定從 0 起算且每次皆加一,但這個索引鍵跟元素值資料並沒有直接關聯; 但在映射表 (map) 中就不是如此,索引鍵 (key) 本身也是資料的一部份,且可以對應到元素值 (value) 。
索引鍵 (key) 可以是任意型別,但不能是切片,因為切片與切片不能相互比較 ; 而元素值 (value) 就真的可以是任意型別,無論陣列、切片、或是另一個 map 都是可以的。
至於如何取得 map 內元素,其實跟陣列切片異曲同工之妙,使用中括號在之中填入要取得的索引鍵 (key),如: [key]
。補充:
正常來說有序資料建議放在陣列或是切片,因為當我們使用 for range 迴圈走訪 map 時,會將資料順序打亂,這時即便你將索引鍵 (key) 按照順序排列,也還是難逃被打亂的命運 ( for range 迴圈詳細可見 DAY 10 ) 。
map[<key 型別>]<value 型別>
在 Go 語言中 map 不會幫我們初始化,且 map 的零值就是 nil ,所以當我們定義 map 時,需要一併賦予其 索引鍵 (key) 跟 元素值 (value) ; 如果硬要對於一個沒有初始化的 map 賦值,執行時會引發 Go 語言中的 panic (之後章節會詳細介紹),我先簡單將其理解為 panic 會將函式的流程中斷,所以 不建議定義一個未經初始化 (都是零值 nil ) 的 map。
map[<key 型別>]<value 型別>{<key 1>:<value 1>, <key 2>:<value 2>, ..., <key N>:<value N>}
跟切片一樣,我們也可以使用內建函式 make() 來定義一個初始化的 map :
make(map[<key 型別>]<value 型別>, <容量>)
與跟切片的 make() 函式不同之處在於,我們不需要給 make() 指定特定長度,因為 Go 語言不會給用 make() 定義的 map 索引鍵 key , map 的容量是可填也可以省略的,若想查詢 map 的容量,我們應該選擇 len(),而不是cap(),因為 cap 是返回切片的容量,並不適用在 map 。
補充:
map 跟切片一樣,若將一個 map 複製,而舊 map 與新 map 是指向同一個底層資料,為了避免又發生我們不曉得指向同一個 map 的問題,建議可以使用迴圈走訪 map 將 key 與 value 寫進新的 map。
當 map 初始化完,想要將其加入新的 key 與 value 是使用 [] ,且不用擔心長度問題,想新增就新增。
<map 名稱>[<索引的 key>] = <value>
範例 1:
package main
import "fmt"
func main(){
myFavorite := map[string]string{ // 定義一個 map ,且賦予其索引鍵 (key) 跟元素值 (value)來初始化 map
"fruit":"mango",
"animal":"panda",
"dessert":"cheesecake",
}
myFavorite["weather"] = "sunny" // 使用 [] 加入要新增進 map 的 key 與 value
fmt.Println("myFavorite",myFavorite)
for key, value := range myFavorite{
fmt.Println("key:",key," = value:",value) // 將 key 與 value 一一印出來
}
}
範例 1(執行結果):
myFavorite map[animal:panda dessert:cheesecake fruit:mango weather:sunny]
key: fruit = value: mango
key: animal = value: panda
key: dessert = value: cheesecake
key: weather = value: sunny
若要從 map 裡取用 value 時,需要使用索引值 key 在 map 中取用資料,若是該索引值 key 不存在, Go 語言會回傳該 map 資料型別的零值,所以有時後我們也可以透過回傳的零值來判斷該索引機是否存在,但特別注意的是這並不一定是完全正確的,因為我們也可以自己將索引值 value 設為該型別的零值,也就是說它可以是代表有意義的零值資料。
至於要怎麼分辨零值資料的索引值 key 是不是真的存在,可以在 map 多接收一個參數 存在狀態,存在狀態是一個布林值,如果 map 含有當個 key ,存在狀態就會是 true ,反之為 false。
<值>, <存在狀態> := <map 名稱>[<索引鍵>]
範例 2:
package main
import (
"fmt"
)
func main() {
myFavorite := map[string]string{ // 定義一個 map ,且賦予其索引鍵 (key) 跟元素值 (value)來初始化 map
"fruit":"mango",
"animal":"panda",
"dessert":"cheesecake",
}
favoriteTitle, exists := myFavorite["fruit"] // 讀取 myFavorite 這個 map 的 key 值 fruit 的 value
if exists { // 如果 fruit 這個 key 有相對應的 value
fmt.Println("目前列表:")
for key, value := range myFavorite { // 用 for range 走訪
fmt.Println("key:",key," = value:",value)
}
}else{
fmt.Println("查無此品項",favoriteTitle)
}
fmt.Println("查詢品項:",favoriteTitle)
}
範例 2(執行結果):
目前列表:
key: fruit = value: mango
key: animal = value: panda
key: dessert = value: cheesecake
查詢品項: mango
在 Go 語言的複合型別中,陣列因為長度固定,故元素不可刪除,只能將其設為零值 ; 在切片中,一樣可以將元素設為零值,或是使用 append() 創造擷取新的一段切片 ; 在 map 中即便你將元素值改為零值,該元素依然存在,且也無法使用 append() 來製造新的 map ,所以若要刪除 map 元素值,需要使用 Go 語言內建 delete() 函式,且。delete() 函式是沒有回傳值的,如果不小心刪除到不存在的索引值,也不會有問題或是報錯,最後 delete() 函式只能搭配 map ,不能搭配陣列或切片。
delete(<map 名稱>, <索引鍵>)
package main
import (
"fmt"
)
var myFavorites = map[string]string {
"fruit":"mango",
"animal":"panda",
"dessert":"cheesecake",
"weather":"sunny",
}
func delleteMyFavorite(title string)() {
delete(myFavorites,title)
}
func main() {
favoriteTitle := myFavorites["fruit"]
delleteMyFavorite(favoriteTitle)
fmt.Println("我的最愛:",myFavorites)
}
我的最愛: map[animal:panda dessert:cheesecake fruit:mango weather:sunny]
今天介紹了 Go 語言 的複合型別映射表 (map) 的所有用法,那我們明天繼續學習如何自訂型別,以及在 Go 語言中結構 (struct) 是什麼,明天見!